package com.guchenbo.example.mybatis;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import javax.sql.DataSource;
import java.util.Map;

public class DaoTest {

  private SqlSessionFactory sqlSessionFactory;

  private Environment devEnv;
  private Environment testEnv;

  @BeforeEach
  public void setUp() {
    devEnv = buildEnvInDev();
    testEnv = buildEnvInTest();
    Configuration configuration = new Configuration(devEnv);
    Assert.assertNotNull(configuration.getEnvironment());

    sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
  }

  private Environment buildEnvInTest() {
    String id = "test";

    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    String driver = "com.mysql.cj.jdbc.Driver";
    String url = "jdbc:mysql://192.168.210.18:3306/financing_1.3.9?useUnicode=true&characterEncoding=utf8";
    String username = "test";
    String password = "kaifabuzhunlian!!!";
    DataSource dataSource = new PooledDataSource(driver, url, username, password);

    return new Environment(id, transactionFactory, dataSource);
  }

  private Environment buildEnvInDev() {
    String id = "dev";

    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://172.30.11.70:3307/financingkf?useUnicode=true&characterEncoding=utf8";
    String username = "dev_write";
    String password = "Aisino@dev#31";
    DataSource dataSource = new PooledDataSource(driver, url, username, password);

    return new Environment(id, transactionFactory, dataSource);
  }

  @Test
  public void test() {
    //manual
    Configuration configuration = sqlSessionFactory.getConfiguration();
    String statementId = "test";
    String sql = "SELECT * FROM credit WHERE id='DEE14CF1DAE34DF9BF1B373CA128DA0E'";
    SqlConfig sqlConfig = new SqlConfig(sql, null, Maps.newHashMap(), SqlCommandType.SELECT);
    configuration.addMappedStatement(buildMappedStatement(configuration, statementId, sqlConfig));

    sqlSessionFactory.openSession().select(statementId, (resultContext) -> {
      System.out.println(resultContext.getResultObject());
    });
  }

  @Test
  public void tesChangeEnv() {
    //manual
    Configuration configuration = sqlSessionFactory.getConfiguration();
    String statementId = "test";
    String sql = "SELECT * FROM credit WHERE id=#{id}";
    SqlConfig sqlConfig = new SqlConfig(sql, String.class, Maps.newHashMap(),
        SqlCommandType.SELECT);
    configuration.addMappedStatement(buildMappedStatement(configuration, statementId, sqlConfig));

    String id = "DEE14CF1DAE34DF9BF1B373CA128DA0E";
    sqlSessionFactory.openSession().select(statementId, id, (resultContext) -> {
      System.out.println(resultContext.getResultObject());
    });

    configuration.setEnvironment(testEnv);
    id = "00216B65879846A281E0437F5389A6C0";
    sqlSessionFactory.openSession().select(statementId, id, (resultContext) -> {
      System.out.println(resultContext.getResultObject());
    });
  }

  @Test
  public void withProxy() {
    Configuration configuration = sqlSessionFactory.getConfiguration();
    String statementId = "test";
    String sql = "SELECT * FROM credit WHERE id='DEE14CF1DAE34DF9BF1B373CA128DA0E'";
    SqlConfig sqlConfig = new SqlConfig(sql, null, Maps.newHashMap(), SqlCommandType.SELECT);
    configuration.addMappedStatement(buildMappedStatement(configuration, statementId, sqlConfig));
// TODO: 2020/7/3
  }

  class SqlConfig {

    String sql;
    Class parameterType;
    Map<String, Object> arguments;
    SqlCommandType sqlCommandType;

    public SqlConfig(String sql, Class parameterType,
        Map<String, Object> arguments, SqlCommandType sqlCommandType) {
      this.sql = sql;
      this.parameterType = parameterType;
      this.arguments = arguments;
      this.sqlCommandType = sqlCommandType;
    }

    public SqlSource toSqlSource(Configuration configuration) {
      return new SqlSourceBuilder(configuration).parse(sql, parameterType, arguments);
    }
  }

  MappedStatement buildMappedStatement(Configuration configuration, String id,
      SqlConfig sqlConfig) {
    // sql
    SqlSource sqlSource = sqlConfig.toSqlSource(configuration);

    // result
    Class resultType = Map.class;
    ResultMap resultMap = new ResultMap.Builder(configuration, id + "ResultMap", resultType,
        Lists.newArrayList())
        .build();

    MappedStatement.Builder builder = new MappedStatement.Builder(configuration, id, sqlSource,
        sqlConfig.sqlCommandType);
    builder.resultMaps(Lists.newArrayList(resultMap));

    return builder.build();
  }

}
